#include "Regla.h"
#include "Log.h"
#include <iostream>
#include <utility>
#include "Configuracion.h"
#include <string>
#include "candies/Button.h"
#include "candies/GoldenStar.h"
#include "candies/MiniBar.h"

#include "Constantes.h"

Regla::Regla(Candy *** tablero, int cantFilas,
             int cantColumnas) : tablero(tablero),
    cantFilas(cantFilas),
    cantColumnas(cantColumnas) { }

Regla::~Regla() { }


int Regla::aplicar(std::pair<int, int> origen) {
    int puntos_celda = 0, puntos_totales = 0;
    Log::loguear(MDEBUG, "Aplicando reglas...");
    // JsonName de los candies (i,j) y sus adyacentes>
    std::string jncandy, jc_ady1, jc_ady2, jc_ady3, jc_ady4;
    // Color del candy (i,j) y sus adyacentes
    Color color, color_ady1, color_ady2, color_ady3, color_ady4;
    // Candies adyacentes al actual en la fila
    Candy *cady1_fil, *cady2_fil, *cady3_fil, *cady4_fil;
    // Candies adyacentes al actual en la columna
    Candy *cady1_col, *cady2_col, *cady3_col, *cady4_col;
    std::string tipo;
    for (int i = 0; i < cantFilas; i++) {
        for (int j = 0; j < cantColumnas; j++) {
            puntos_celda = 0;
            // Para la derecha
            if (tablero[i][j] == NULL || !tablero[i][j]->movible()) {
                continue;
            }

            if (origen.first==-1 || origen.second ==-1){
              origen.first=i;
              origen.second=j;
            }

            jncandy = tablero[i][j]->getJsonName();
            color = tablero[i][j]->getColor();
            tipo= tablero[i][j]->getNameAsString();

            cady1_fil= cady2_fil = cady3_fil = cady4_fil = NULL;
            cady1_col= cady2_col = cady3_col = cady4_col = NULL;

            if (j + 2 < cantColumnas) {
                if (tablero[i][j + 1] && tablero[i][j + 1]->movible())
                  cady1_fil = tablero[i][j + 1];
                if (tablero[i][j + 2] && tablero[i][j + 2]->movible())
                   cady2_fil = tablero[i][j + 2];
            }

            if (j + 3 < cantColumnas) {
              if (tablero[i][j + 3] && tablero[i][j + 3]->movible())
                cady3_fil = tablero[i][j + 3];
            }

            if (j + 4 < cantColumnas) {
              if (tablero[i][j + 4] && tablero[i][j + 4]->movible())
                cady4_fil = tablero[i][j + 4];
            }

            if (i + 2 < cantFilas) {
              if (tablero[i + 1][j] && tablero[i + 1][j]->movible())
                cady1_col = tablero[i + 1][j];

              if (tablero[i + 2][j] && tablero[i + 2][j]->movible())
                cady2_col = tablero[i + 2][j];
            }

            if (i + 3 < cantFilas) {
              if (tablero[i + 3][j] && tablero[i + 3][j]->movible())
                cady3_col = tablero[i + 3][j];
            }

            if (i + 4 < cantFilas) {
              if (tablero[i + 4][j] && tablero[i + 4][j]->movible())
                cady4_col = tablero[i + 4][j];
            }

            if (tipo == BUTTON){
                if (j + 2 < cantColumnas && cady1_fil && cady2_fil &&
                  jncandy.compare((jc_ady1 = cady1_fil->getJsonName())) == 0 &&
                  jncandy.compare((jc_ady2 = cady2_fil->getJsonName())) == 0) {

                  if (j + 3 < cantColumnas  && cady3_fil &&
                   jncandy.compare((jc_ady3 = cady3_fil->getJsonName())) == 0){

                    if (j + 4 < cantColumnas && cady4_fil &&
                      jncandy.compare((jc_ady4 = cady4_fil->getJsonName()))==0){
                      puntos_celda += cincoBotonesMismoColor(origen,
                                                       std::pair<int, int>(i, j),
                                                                        DERECHA);
                    } else {
                      puntos_celda += cuatroBotonesMismoColor(origen,
                                                       std::pair<int, int>(i, j),
                                                                        DERECHA);
                    }
                  } else {
                     puntos_celda+=tresBotonesMismoColor(std::pair<int, int>(i, j),
                                                                        DERECHA);
                  }
                    // Para abajo
                } else if (i + 2 < cantFilas &&  cady1_col && cady2_col &&
                        jncandy.compare((jc_ady1 = cady1_col->getJsonName()))==0 &&
                        jncandy.compare((jc_ady2 = cady2_col->getJsonName()))==0){

                    if (i + 3 < cantFilas && cady3_col &&
                        jncandy.compare((jc_ady3 = cady3_col->getJsonName()))==0){
                        if (i + 4 < cantFilas && cady4_col &&
                      jncandy.compare((jc_ady4 = cady4_col->getJsonName())) == 0) {
                        puntos_celda += cincoBotonesMismoColor(origen,
                                                         std::pair<int, int>(i, j),
                                                                            ABAJO);
                      } else {
                        puntos_celda += cuatroBotonesMismoColor(origen,
                                                        std::pair<int, int>(i, j),
                                                                            ABAJO);
                      }
                    } else {
                        puntos_celda += tresBotonesMismoColor(
                                                        std::pair<int, int>(i, j),
                                                                            ABAJO);
                    }
                  }

            }
            // Chequeo por color para la derecha
            if (puntos_celda==0){
              if (j + 2 < cantColumnas  &&  cady1_fil && cady2_fil &&
                         color == (color_ady1 = cady1_fil->getColor())
                         && color == (color_ady2 = cady2_fil->getColor())) {
                if (((j + 3) < cantColumnas) && cady3_fil &&
                        (color == (color_ady3 = cady3_fil->getColor()))) {
                  if (((j + 4) < cantColumnas) && cady4_fil &&
                          (color == (color_ady4 = cady4_fil->getColor()))) {
                   puntos_celda+=cuatroOMasMismoColor(std::pair<int, int>(i, j),
                                                                    DERECHA, 5);
                  } else {
                   puntos_celda+=cuatroOMasMismoColor(std::pair<int, int>(i, j),
                                                                    DERECHA, 4);
                  }
                }
              // Para abajo
              } else if (i + 2 < cantFilas && cady1_col && cady2_col &&
                         color == (color_ady1 = cady1_col->getColor())
                         && color == (color_ady2 = cady2_col->getColor())) {
                if (i + 3 < cantFilas && cady3_col &&
                            color == (color_ady3 = cady3_col->getColor())) {
                  if (i + 4 < cantFilas && cady4_col &&
                          color == (color_ady4 = cady4_col->getColor())) {
                   puntos_celda+=cuatroOMasMismoColor(std::pair<int, int>(i, j),
                                                                      ABAJO, 5);
                  } else {
                   puntos_celda+=cuatroOMasMismoColor(std::pair<int, int>(i, j),
                                                                      ABAJO, 4);
                  }
                }
              }
          }

          puntos_totales += puntos_celda;
          if (MODO_DEBUG == 1 && puntos_celda > 0)
              std::cout << " > Ubicacion aprox.: (Fila,Columna): " << i <<
                           "," << j << std::endl;
        }
    }

    return puntos_totales;
}

/*****************************************************************************/
/*                      Botones                                              */
/*****************************************************************************/

/* Si hay 3 del mismo color los elimino nomas */
int Regla::tresBotonesMismoColor(std::pair<int, int> posInicial,
                                 Direccion direccion) {
    Log::loguear(MDEBUG, "<REGLA 1> aplicando tresBotonesMismoColor...");
    if (direccion == DERECHA) {
        for (int j = posInicial.second; j < posInicial.second + 3; j++) {
            eliminarPosicion(posInicial.first, j);
        }
    } else {
        for (int i = posInicial.first; i < posInicial.first + 3; i++) {
            eliminarPosicion(i, posInicial.second);
        }
    }
    return 30;
}

/* Si hay 4 del mismo color, el que genera el movimiento se convierte
   en miniBar */
int Regla::cuatroBotonesMismoColor(std::pair<int, int> posCandy,
                                   std::pair<int, int> posInicial,
                                   Direccion direccion) {
    Log::loguear(MDEBUG, "<REGLA 2> aplicando cuatroBotonesMismoColor...");

    /* Si no se formó a partir de un movimiento del jugador se reemplaza
       por minibar la primer posicion */
    if (posCandy.first == -1 || posCandy.second == -1) {
        posCandy = posInicial;
    }

    if (direccion == DERECHA) {
        for (int j = posInicial.second; j < posInicial.second + 4; j++) {
          if (posCandy != std::pair<int, int>(posInicial.first, j)) {
            eliminarPosicion(posInicial.first, j);
          } else {
            Candy *miniBar = obtenerMiniBar(
                     tablero[posCandy.first][posCandy.second]->getColor());

            if (direccion== ABAJO || direccion == ARRIBA)
              ((MiniBar*)miniBar)->setOrientacion(VERTICAL);
            else
              ((MiniBar*)miniBar)->setOrientacion(HORIZONTAL);

            delete tablero[posCandy.first][posCandy.second];

            miniBar->setMagico();
            tablero[posCandy.first][posCandy.second] = miniBar;
          }
        }
    } else {
        for (int i = posInicial.first; i < posInicial.first + 4; i++) {
          if (posCandy != std::pair<int, int>(i, posInicial.second)) {
              eliminarPosicion(i, posInicial.second);
          } else {
              Candy *miniBar = obtenerMiniBar(
                    tablero[posCandy.first][posCandy.second]->getColor());

              if (direccion== ABAJO || direccion == ARRIBA)
                ((MiniBar*)miniBar)->setOrientacion(VERTICAL);
              else
                ((MiniBar*)miniBar)->setOrientacion(HORIZONTAL);

              delete tablero[posCandy.first][posCandy.second];

              miniBar->setMagico();
              tablero[posCandy.first][posCandy.second] = miniBar;
          }
        }
    }

    return 80;
}

/* Si hay 5 del mismo color, el que genera el movimiento se convierte
   en goldenStar */
int Regla::cincoBotonesMismoColor(std::pair<int, int> posCandy,
                                  std::pair<int, int> posInicial,
                                  Direccion direccion) {
    Log::loguear(MDEBUG, "<REGLA 3> aplicando cincoBotonesMismoColor...");
    /* Si no se formó a partir de un movimiento del jugador se reemplaza
       por goldenstar la primer posicion */
    if (posCandy.first == -1 || posCandy.second == -1) {
        posCandy = posInicial;
    }

    if (direccion == DERECHA) {
        for (int j = posInicial.second; j < posInicial.second + 5; j++) {
            if (posCandy != std::pair<int, int>(posInicial.first, j)) {
                eliminarPosicion(posInicial.first, j);
            } else {
                Candy *goldenStar = new GoldenStar(NONE, GOLDENSTAR);

                delete tablero[posCandy.first][posCandy.second];

                goldenStar->setMagico();
                tablero[posCandy.first][posCandy.second] = goldenStar;
            }
        }
    } else {
        for (int i = posInicial.first; i < posInicial.first + 5; i++) {
            if (posCandy != std::pair<int, int>(i, posInicial.second)) {
                eliminarPosicion(i, posInicial.second);
            } else {
                Candy *goldenStar = new GoldenStar(NONE, GOLDENSTAR);

                delete tablero[posCandy.first][posCandy.second];

                goldenStar->setMagico();
                tablero[posCandy.first][posCandy.second] = goldenStar;
            }
        }
    }

    return 150;
}

/*****************************************************************************/
/*                      Variados                                             */
/*****************************************************************************/

int Regla::cuatroOMasMismoColor(std::pair<int, int> posInicial,
                                Direccion direccion, int cantidad) {
    Log::loguear(MDEBUG, "<REGLA 4> aplicando cuatroOMasMismoColor...");
    Candy *candy;
    std::string jsonName;
    int cantEliminados = 0;
    MiniBar* minibar;

    if (direccion == DERECHA) {
      for (int j = posInicial.second; j < cantidad + posInicial.second; j++) {
        if ((candy = tablero[posInicial.first][j]) != NULL) {
            jsonName = candy->getJsonName();
            // Si la cantidad es mayor a 3, las minibar eliminan la fila entera.
           if (cantidad > 3 && jsonName.find("MiniBar") != std::string::npos) {
               minibar=(MiniBar*) candy;
               if (minibar->getOrientacion()==HORIZONTAL)
                cantEliminados += eliminarFila(posInicial.first);
               else
                cantEliminados += eliminarColumna(j);
           } else {
               cantEliminados += eliminarPosicion(posInicial.first, j);
           }
        }
      }
    } else {
        // Recorre columna.
        for (int i = posInicial.first; i < cantidad + posInicial.first; i++) {
         if ((candy = tablero[i][posInicial.second]) != NULL) {
             jsonName = candy->getJsonName();
         // Si la cantidad es mayor a 3, las minibar eliminan la fila entera.
         if (cantidad > 3 && jsonName.find("MiniBar") != std::string::npos) {
               minibar=(MiniBar*) candy;
               if (minibar->getOrientacion()==HORIZONTAL)
                cantEliminados += eliminarFila(i);
               else
                cantEliminados += eliminarColumna(posInicial.second);
         } else {
             cantEliminados += eliminarPosicion(i, posInicial.second);
         }
         }
        }
    }

    return 10 * cantEliminados;
}

/*****************************************************************************/
/*                      Golden Star                                          */
/*****************************************************************************/

int Regla::goldenStar(std::pair<int, int> origen, std::pair<int, int> destino){
    Log::loguear(MDEBUG, "<REGLA 0> aplicando goldenStar...");

    std::string jsonNameOrigen =
        tablero[origen.first][origen.second]->getJsonName();
    std::string jsonNameDestino =
        tablero[destino.first][destino.second]->getJsonName();

    if (jsonNameOrigen.compare(GOLDENSTAR) == 0) {
        if (jsonNameDestino.compare(GOLDENSTAR) == 0) {
          return goldenStarMasGoldenStar();
        } else if (jsonNameDestino.find("MiniBar") != std::string::npos) {
          return goldenStarMasMiniBar(destino);
        } else {
          return goldenStarMasButton(destino) + eliminarPosicion(origen.first,
                    origen.second);
        }
    } else if (jsonNameOrigen.find("MiniBar") != std::string::npos) {
        return goldenStarMasMiniBar(origen);
    } else {
        return goldenStarMasButton(origen) + eliminarPosicion(destino.first,
                destino.second);
    }
}

int Regla::goldenStarMasButton(std::pair<int, int> button) {
 Log::loguear(MDEBUG, "<REGLA 0/1> aplicando goldenStarMasButton...");
return 40 * eliminarColor(tablero[button.first][button.second]->getColor());
}

int Regla::goldenStarMasMiniBar(std::pair<int, int> miniBar) {
 Log::loguear(MDEBUG, "<REGLA 0/2> aplicando goldenStarMasMiniBar...");
 int puntos= eliminarColor(tablero[miniBar.first][miniBar.second]->getColor());
 puntos += eliminarFila(miniBar.first);
 puntos += eliminarColumna(miniBar.second);
return 40 * puntos;
}

int Regla::goldenStarMasGoldenStar() {
    Log::loguear(MDEBUG, "<REGLA 0/3> aplicando goldenStarMasGoldenStar...");
    return 40 * eliminarTodo();
}

/*****************************************************************************/
/*                      Funciones auxiliares                                 */
/*****************************************************************************/

Candy *Regla::obtenerMiniBar(Color color) {
    std::string jsonName;
    switch (color) {
    case BLUE:
        jsonName = BLUE_MINIBAR;
        break;
    case GREEN:
        jsonName = GREEN_MINIBAR;
        break;
    case RED:
        jsonName = RED_MINIBAR;
        break;
    case VIOLET:
        jsonName = VIOLET_MINIBAR;
        break;
    case YELLOW:
    case NONE:
    default:
        jsonName = YELLOW_MINIBAR;
        break;
    }
return new MiniBar(color, jsonName);
}

int Regla::eliminarColor(Color color) {
    int cantidad = 0;

    for (int i = 0; i < cantFilas; i++) {
        for (int j = 0; j < cantColumnas; j++) {
            if (tablero[i][j] && tablero[i][j]->getColor() == color) {
                cantidad += eliminarPosicion(i, j);
            }
          }
     }
return cantidad;
}

int Regla::eliminarFila(int fila) {
    int cantidad = 0;

    for (int j = 0; j < cantColumnas; j++) {
        cantidad += eliminarPosicion(fila, j);
    }

return cantidad;
}

int Regla::eliminarColumna(int columna) {
    int cantidad = 0;
    for (int i = 0; i < cantFilas; i++) {
        cantidad += eliminarPosicion(i, columna);
    }
return cantidad;
}

int Regla::eliminarTodo() {
    int cantidad = 0;

    for (int i = 0; i < cantFilas; i++) {
        for (int j = 0; j < cantColumnas; j++) {
              cantidad += eliminarPosicion(i, j);
        }
    }
return cantidad;
}

int Regla::eliminarPosicion(int i, int j) {
    int cantidad = 0;

    if (tablero[i][j] && tablero[i][j]->eliminable()) {
        delete tablero[i][j];
        tablero[i][j] = NULL;
        cantidad++;
    }
return cantidad;
}
